package com.example.demo.zip.test;

import com.example.demo.util.ResultUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.apache.tools.zip.ZipOutputStream;

import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

import static org.apache.catalina.startup.ExpandWar.deleteDir;

/**
 * @Author: zjq
 * @Date: create in 2022/9/22 14:57
 */
public class ZipUtil {

    //引入的依赖
    // <dependency>
    //    <groupId>org.apache.ant</groupId>
    //    <artifactId>ant</artifactId>
    //    <version>1.10.5</version>
    //</dependency>
    private static final int buffer = 2048;

    /**
     * zip、rar
     * 13.7MB----->12MB
     * 速度在1250-2030之间，两者相差不多
     *
     * @param args
     */
    // 测试---下载到本地
    public static void main(String[] args) {
        Long start = System.currentTimeMillis();
        String path = "C:\\Users\\THINK\\Downloads\\test";
        String path2 = "a12";

        try {
            //1.压缩
            generateFile(path, "zip", path2);

            //2.解压文件夹
            unZip(path, null);

            //3.读取压缩包里面的所有文件
            List<String> str = new ArrayList<>();
            str = (List<String>) readZipFile(path).get("data");
            System.out.println(str);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
        }
        Long end = System.currentTimeMillis();
        System.out.println("start" + start);
        System.out.println("end" + end);
        System.out.println("start-end" + (start - end));
    }


    /**
     * @param path        要压缩的文件路径
     * @param format      生成的格式(zip、rar)
     * @param zipFileName 生成压缩包的文件名
     * @function 将指定的文件压缩成指定文件名和格式的样式
     */
    public static void generateFile(String path, String format, String zipFileName) throws Exception {

        File file = new File(path);
        // 压缩文件的路径不存在
        if (!file.exists()) {
            throw new Exception("路径 " + path + " 不存在文件，无法进行压缩...");
        }
        // 用于存放压缩文件的文件夹
        String generateFile = file.getParent() + File.separator;
        File compress = new File(generateFile);
        // 如果文件夹不存在，进行创建
        if (!compress.exists()) {
            compress.mkdirs();
        }

        // 目的压缩文件
        String generateFileName = compress.getAbsolutePath() + File.separator + zipFileName + "." + format;

        // 输入流 表示从一个源读取数据
        // 输出流 表示向一个目标写入数据

        // 输出流
        FileOutputStream outputStream = new FileOutputStream(generateFileName);

        // 压缩输出流
        ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(outputStream));
        zipOutputStream.setEncoding("GBK");
        generateFile(zipOutputStream, file, "");

        System.out.println("源文件位置：" + file.getAbsolutePath() + "，目的压缩文件生成位置：" + generateFileName);
        // 关闭 输出流
        zipOutputStream.close();
        //删除压缩文件
        deleteDir(new File(path));
    }

    /**
     * @param out  输出流
     * @param file 目标文件
     * @param dir  文件夹
     * @throws Exception
     */
    private static void generateFile(ZipOutputStream out, File file, String dir) throws Exception {

        // 当前的是文件夹，则进行一步处理
        if (file.isDirectory()) {
            //得到文件列表信息
            File[] files = file.listFiles();

            //将文件夹添加到下一级打包目录
            out.putNextEntry(new ZipEntry(dir + "/"));

            dir = dir.length() == 0 ? "" : dir + "/";

            //循环将文件夹中的文件打包
            for (int i = 0; i < files.length; i++) {
                generateFile(out, files[i], dir + files[i].getName());
            }
        } else { // 当前是文件
            // 输入流
            FileInputStream inputStream = new FileInputStream(file);
            // 标记要打包的条目
            out.putNextEntry(new ZipEntry(dir));
            // 进行写操作
            int len = 0;
            byte[] bytes = new byte[1024];
            while ((len = inputStream.read(bytes)) > 0) {
                out.write(bytes, 0, len);
            }
            // 关闭输入流
            inputStream.close();
        }
    }


    /**
     * 解压zip
     *
     * @param zipPath      zip文件路径 必传
     * @param saveFilePath 如果为空那么解压到zipPath的当前目录,不为空解压到指定目录
     */
    public static void unZip(String zipPath, String saveFilePath) {
        int count = -1;
        String savepath = "";
        File file = null;
        InputStream is = null;
        FileOutputStream fos = null;
        BufferedOutputStream bos = null;
        //保存解压文件目录
        if (StringUtils.isNotBlank(saveFilePath)) {
            savepath = new File(saveFilePath) + File.separator;
        } else {
            savepath = new File(zipPath).getParent() + File.separator;
        }
        new File(savepath).mkdir(); //创建保存目录
        ZipFile zipFile = null;
        try {
            //解决中文乱码问题  格式有GBK  UTF8
            zipFile = new ZipFile(zipPath, "GBK");
            Enumeration<?> entries = zipFile.getEntries();
            while (entries.hasMoreElements()) {
                byte buf[] = new byte[buffer];
                ZipEntry entry = (ZipEntry) entries.nextElement();
                String filename = entry.getName();
                boolean ismkdir = false;
                //检查此文件是否带有文件夹
                if (filename.lastIndexOf("/") != -1) {
                    ismkdir = true;
                }
                filename = savepath + filename;
                //如果是文件夹先创建
                if (entry.isDirectory()) {
                    file = new File(filename);
                    file.mkdirs();
                    continue;
                }
                file = new File(filename);
                if (!file.exists()) {
                    //如果是目录先创建
                    if (ismkdir) {
                        //目录先创建
                        new File(filename.substring(0, filename.lastIndexOf("/"))).mkdirs();
                    }
                }
                //创建文件
                file.createNewFile();
                is = zipFile.getInputStream(entry);
                fos = new FileOutputStream(file);
                bos = new BufferedOutputStream(fos, buffer);
                while ((count = is.read(buf)) > -1) {
                    bos.write(buf, 0, count);
                }
                bos.flush();
                bos.close();
                fos.close();
                is.close();
            }
            zipFile.close();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } finally {
            try {
                if (bos != null) {
                    bos.close();
                }
                if (fos != null) {
                    fos.close();
                }
                if (is != null) {
                    is.close();
                }
                if (zipFile != null) {
                    zipFile.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 读取zip中所有文件的名称
     *
     * @param path zip文件路径
     * @return
     * @throws IOException
     */
    public static Map<String, Object> readZipFile(String path) throws IOException {
        List<String> str = new ArrayList<>();
        java.util.zip.ZipEntry zipEntry = null;
        File file = new File(path);
        if (file.exists()) { //判断文件是否存在
            ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(path), Charset.forName("GBK")); //解决包内文件存在中文时的中文乱码问题
            while ((zipEntry = zipInputStream.getNextEntry()) != null) {
                if (zipEntry.isDirectory()) { //遇到文件夹就跳过
                    //continue;
                    zipInputStream.close();
                    return ResultUtil.info(-1, "压缩包中不能包含文件夹");
                } else {
                    //str += ";" + zipEntry.getName().substring(zipEntry.getName().lastIndexOf("/") + 1);
                    str.add(zipEntry.getName().substring(zipEntry.getName().lastIndexOf("/") + 1));
                    //通过getName()可以得到文件名称
                    //System.out.println(zipEntry.getName().substring(zipEntry.getName().lastIndexOf("/")+1));
                }
            }
            zipInputStream.close();
        }
        return ResultUtil.success("data", str);
    }
}
